package com.ruoyi.wss;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author 阿童木
 */
@Slf4j
@Service
public class WebSocketMessageService {

    // 保存“等待响应”的映射：platformId -> CompletableFuture
    public static final ConcurrentHashMap<String, CompletableFuture<JSONObject>> WAIT_MAP = new ConcurrentHashMap<>();
    // 默认10秒超时
    private static final long DEFAULT_TIMEOUT = 10;

    @Transactional(rollbackFor = Exception.class)
    public void handle(String platformId, String json) {
        JSONObject jsonObject = JSON.parseObject(json);
        String cmdType = jsonObject.getString("cmdType");
        if("keepalive".equals(cmdType)){
            handleHeartbeat(platformId, jsonObject);
        }else {
            WAIT_MAP.computeIfPresent(platformId, (key, future) -> {
                if (!future.isDone()) {
                    try {
                        future.complete((JSONObject) jsonObject.get("data"));
                    } catch (Exception e) {
                        future.completeExceptionally(e);
                    }
                }
                return null; // 移除映射
            });
        }
    }

    /**
     * 心跳
     * @param platformId
     * @param jsonObject
     */
    private void handleHeartbeat(String platformId, JSONObject jsonObject) {
        JSONObject jo = new JSONObject();
        jo.put("sn", jsonObject.get("sn"));
        jo.put("status", "OK");
        jo.put("message", "");
        jo.put("cmdType", "keepalive");
        sendToDevice(platformId, jo.toString());
    }

    /**
     * 发送消息，返回结果
     * @param platformId 连接id
     * @param request 构建的请求
     * @return
     * @throws Exception
     */
    public <T> T queryDevice(String platformId, Object request, Class<T> responseType) throws Exception {
        JSONObject responseJson = sendAndWait(platformId, request);
        if (!"0000".equals(responseJson.get("code"))) {
            throw new RuntimeException(responseJson.get("errMsg").toString());
        }
        return JSON.parseObject(responseJson.get("data").toString(), responseType);
    }

    public Boolean operationDevice(String platformId, Object request) throws Exception {
        JSONObject responseJson = sendAndWait(platformId, request);
        if (!"0000".equals(responseJson.getString("code"))) {
            throw new RuntimeException(responseJson.getString("errMsg"));
        }
        return responseJson.getBoolean("success");
    }

    /**
     * 发送和等待方法
     */
    private JSONObject sendAndWait(String platformId, Object request) throws Exception {
        CompletableFuture<JSONObject> future = new CompletableFuture<>();

        // 保存future到映射
        WAIT_MAP.put(platformId, future);

        try {
            // 通过 WebSocket 发送
            String message = JsonUtil.toJson(request);
            sendToDevice(platformId, message);

            // 阻塞等待终端回包（带超时）
            return future.get(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        } finally {
            // 清理资源
            WAIT_MAP.remove(platformId);
        }
    }

    /**
     * 给指定设备发消息
     */
    public boolean sendToDevice(String platformId, String message) {
        Session session = WebSocketServer.LOCAL_SESSIONS.get(platformId);
        if (session == null || !session.isOpen()) {
            return false;
        }
        try {
            session.getBasicRemote().sendText(message);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 广播给所有在线设备
     */
    public void broadcast(String message) {
        for (Map.Entry<String, Session> entry : WebSocketServer.LOCAL_SESSIONS.entrySet()) {
            sendToDevice(entry.getKey(), message);
        }
    }

}
